#include "OP_Duplicate.h"
#include "../Geometries/PointCloud.h"
#include "../Geometries/PolygonMesh.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

///<************************************************************************************************************>
///<************************************* region CONSTANTS DEFINITION ******************************************>
///<************************************************************************************************************>

/***************************************************************************************************************
************************************** endregion CONSTANTS DEFINITION ******************************************
****************************************************************************************************************/












/// <summary> 
/// Constructor 
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::OP_Duplicate::OP_Duplicate (vector<CIView::View*>& v)
: Operation (v) {}

/// <summary> 
/// Destructor 
/// </summary>
CIOps::OP_Duplicate::~OP_Duplicate() {
    if (status != COMPLETED)            
        //Deleting all the created geometries
        for (int i=0; i<duplicatedGeometries.size(); i++) 
            delete duplicatedGeometries[i];          

}











///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to execute directly the operation 
/// </summary>
/// <returns> The operation status </returns>
int CIOps::OP_Duplicate::Exec() {
    SaveOldSelection();                             //Saves the old selection status
    DuplicateSelectedPrimitives();

    //Adding in the scene all the geometries duplicated
    for (int i=0; i<duplicatedGeometries.size(); i++) 
        scene.AddGeometry (duplicatedGeometries[i]);
    
    SaveNewSelection();                     //Saves the new selection status

    status = COMPLETED;

    UpdateViews();                          //Updates the 3D views

    return status;        
}

/// <summary> 
/// Method called to undo the operation 
/// </summary>
void CIOps::OP_Duplicate::Undo() {
    //Removing all the created geometries
    for (int i=0; i<duplicatedGeometries.size(); i++) 
        scene.RemoveGeometry (duplicatedGeometries[i]->GetName().c_str());  
    
    RestoreOldSelection();                              //Restores the old selection status

    status = UNDO;

    UpdateViews();                          //Updates the 3D views
}

/// <summary> 
/// Method called to redo the operation 
/// </summary>
void CIOps::OP_Duplicate::Redo() {
    //Readding all the created geometries
    for (int i=0; i<duplicatedGeometries.size(); i++) 
        scene.AddGeometry (duplicatedGeometries[i]); 
    
    RestoreNewSelection();                          //Restore the new selectio status

    status = COMPLETED;

    UpdateViews();                          //Updates the 3D views
}

/// <summary> 
/// Mouse Pressed event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse press event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Pressed event </param>
void CIOps::OP_Duplicate::MousePressEvent (CIView::View* view, QMouseEvent* e) {}

/// <summary> 
/// Mouse Released event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse release event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Released event </param>
void CIOps::OP_Duplicate::MouseReleaseEvent (CIView::View* view, QMouseEvent* e) {}

/// <summary> 
/// Mouse Moved event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse move event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Moved event </param>
void CIOps::OP_Duplicate::MouseMoveEvent (CIView::View* view, QMouseEvent* e) {}

/// <summary> 
/// Wheel event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse wheel event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Wheel event </param>
void CIOps::OP_Duplicate::WheelEvent (CIView::View* view, QWheelEvent* e) {}

/// <summary> 
/// Method called to duplicate the primitives currently selected
/// </summary>
void CIOps::OP_Duplicate::DuplicateSelectedPrimitives() {
    switch (scene.GetSelectionMode()){
        case Geometry::VERTICES_SELECTION:
            DuplicateSelectedVertices();
            break;

        case Geometry::FACES_SELECTION:
            DuplicateSelectedFaces();
            break;
    }    
}

/// <summary> 
/// Method called to duplicate the vertices currently selected
/// </summary>
void CIOps::OP_Duplicate::DuplicateSelectedVertices() {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();
    map<const char*, Geometry*>::iterator g = geometries.begin(); 

    Geometry* newGeometry = 0;

    while (g != geometries.end()) {                             //For each geometry
        if (g->second->hasPrimitivesSelected) {

            VertexLib& vertices = g->second->GetVertices();        

            for (int i=0; i<vertices.size(); i++) {                             //For each vertex of this geometry
                if (vertices[i]->selected) {                                    //if the vertex is selected, so it has to be duplicated
                    
                    if (!newGeometry) {
                        unsigned int id = scene.GenerateNewGeometryID();        //Gets a reference to the number of geometries that have been added to the scene
                        stringstream name;
                        name << "PointCloud" << id;                             //Sets the name of the new triangle mesh 

                        newGeometry = new PointCloud (name.str());              //Creates a point cloud 
                    }
                        
                    newGeometry->GetVertices().push_back (new Vertex (*vertices[i]));   //Inserts in the vertex library of the new geometry the duplicated vertex
                    vertices[i]->selected = false;                                      //Deselects the original vertex
                }
            }

            if (newGeometry) {                                          //If a new geometry has been created
                newGeometry->UpdateBoundingBox();                       //updates the geometry bounding box

                duplicatedGeometries.push_back (newGeometry);           //we must save a pointer to it
                newGeometry = 0;
            }
        }

        g++;
    }
}

/// <summary> 
/// Method called to duplicate the faces currently selected
/// </summary>
void CIOps::OP_Duplicate::DuplicateSelectedFaces() {
    map<const char*, Geometry*>& geometries = scene.GetGeometries();
    map<const char*, Geometry*>::iterator g = geometries.begin(); 

    Geometry* newGeometry = 0;

    while (g != geometries.end()) {                             //For each geometry
        if (g->second->hasPrimitivesSelected) {

            map<Vertex*, Vertex*> clone;                            //Hash table to store for each vertex, its clone
            Vertex* v = 0;
            VertexLib& vertices = g->second->GetVertices();        

            for (int i=0; i<vertices.size(); i++) {                             //For each vertex of this geometry
                if (vertices[i]->selected) {                                    //if the vertex is selected, so it has to be duplicated
                    
                    if (!newGeometry) {
                        unsigned int id = scene.GenerateNewGeometryID();        //Gets a reference to the number of geometries that have been added to the scene
                        stringstream name;
                        name << "Mesh" << id;                                   //Sets the name of the new triangle mesh 

                        newGeometry = new PolygonMesh (name.str());             //Creates a new mesh
                    }
                    v = new Vertex (*vertices[i]);                              //Clones this vertex
                    clone[vertices[i]] = v;                                     //Saves a pointer to the clone of this vertex
                    newGeometry->GetVertices().push_back (v);                   //Inserts in the vertex library of the new geometry the duplicated vertex
                    vertices[i]->selected = false;                              //Deselects the original vertex
                }
            }
            
            if (newGeometry) {                                          //If a new geometry has been created
                FaceLib& faces = g->second->GetFaces();
            
                //We have to clone also all its faces
                for (int i=0; i<faces.size(); i++) {                        
                    const VertexLib& faceVertices = faces[i]->vertices;
                    VertexLib clonedVertices;
                    
                    if (faces[i]->selected) {                                       //if the face is selected, it has to be duplicated                                   //if the vertex is selected, so it has to be duplicated
                                                                                                 
                        //we must create a new face, equal to this, so we have to 
                        //clone all its vertices
                        for (int j=0; j<faceVertices.size(); j++) {                 //For each vertex of this face                                                
                            clonedVertices.push_back (clone[faceVertices[j]]);
                        }
                        Face* f = new Face (clonedVertices);
                        f->ComputeNormal();
                        newGeometry->AddFace (f);                                   //Inserts in the face library of the new geometry the duplicated vertex
                        faces[i]->selected = false;                                 //Deselcts the original face
                        clonedVertices.clear();
                    }
                }
                newGeometry->UpdateBoundingBox();              //Updates the geometry bounding box

                duplicatedGeometries.push_back (newGeometry);           //we must save a pointer to it
                newGeometry = 0;
            }
        }
        g++;
    }
}
/***************************************************************************************************************
***************************************** endregion Utility Methods ********************************************
****************************************************************************************************************/
